package han.cirno.corrupt

import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.os.Bundle
import androidx.core.content.ContextCompat
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.doublePreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.floatPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.HiltAndroidApp
import han.cirno.corrupt.activity.GlobalViewModel
import han.cirno.corrupt.activity.ohno.OhnoActivity
import han.cirno.corrupt.components.crash.CrashUtils
import han.cirno.corrupt.compose.updateLocale
import han.cirno.corrupt.data.UserUnstructuredData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.util.Locale
import javax.inject.Inject
import kotlin.system.exitProcess

val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "corrupt")

@HiltAndroidApp
class Application:Application() {
    private val localeContextMap = mutableMapOf<Locale, Context>()
    private var latestLocale: Locale = Locale.getDefault()
    private val crashHandler = CrashHandler()
    private lateinit var receiver: Receiver
    @Inject
    lateinit var globalViewModel: GlobalViewModel
    val sharedPreferences: SharedPreferences
        get() = getSharedPreferences("${packageName}_preferences", MODE_PRIVATE)

    override fun onCreate() {
        super.onCreate()
        application = this
        UserUnstructuredData.init()
        receiver = Receiver()
        ContextCompat.registerReceiver(
            this, receiver,
            IntentFilter("han.cirno.nofy.broadcast.BROADCAST_BACK"), ContextCompat.RECEIVER_EXPORTED
        )
        GlobalViewModel.sendCheckBroadcast(this)
        Thread.setDefaultUncaughtExceptionHandler(crashHandler)
    }

    override fun onTerminate() {
        super.onTerminate()
        unregisterReceiver(receiver)
    }

    companion object {
        lateinit var application: han.cirno.corrupt.Application
        fun getApplicationContext(): Context {
            return application.applicationContext
        }
    }

    fun getLocaleContext(locale: Locale = latestLocale): Context =
        if (locale !in localeContextMap){
            updateLocale(locale).also { localeContextMap[locale] = it }
        }else {
            localeContextMap[locale]!!
        }.also { latestLocale=locale }

    inner class Receiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            if (intent == null) return
            globalViewModel.broadcastCheck.postValue(1)
            val extras = intent.extras ?: return
            globalViewModel.run {
                configLatitude.postValue(extras.getString("latitude", "-"))
                configLongitude.postValue(extras.getString("longitude", "-"))
                configMode.postValue(extras.getString("mode", "-"))
            }
        }
    }

    class CrashHandler : Thread.UncaughtExceptionHandler {
        override fun uncaughtException(t: Thread, e: Throwable) {
            e.printStackTrace()
            val (report,tempFile)=CrashUtils.makeCrashReportFile(t,e)
            val context = getApplicationContext()
            val intent = Intent(context, OhnoActivity::class.java)
            val bundle = Bundle()
            bundle.putString("report", report)
            bundle.putString("path",tempFile.absolutePath)
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
            intent.putExtras(bundle)
            context.startActivity(intent)
            exitProcess(0)
        }
    }
}

fun <T> CoroutineScope.writeDataStore(key: Preferences.Key<T>, value: T){
    launch {
        han.cirno.corrupt.Application.application.dataStore.edit {
            it[key]=value
        }
    }
}

@Suppress("UNCHECKED_CAST")
inline fun <reified T> getPreferenceKey(keyString: String) :Preferences.Key<T> =
    when(T::class){
        Int::class-> intPreferencesKey(keyString)
        String::class-> stringPreferencesKey(keyString)
        Boolean::class-> booleanPreferencesKey(keyString)
        Float::class-> floatPreferencesKey(keyString)
        Double::class-> doublePreferencesKey(keyString)
        Long::class-> longPreferencesKey(keyString)
        else->throw IllegalArgumentException("Not such implement: ${T::class}")
    } as Preferences.Key<T>
